using System;
using System.Collections.Generic;
using System.Text;

using Forecast.Database;

namespace Forecast.Business
{
    public class NewOffer
    {
        #region Variables

        private Player _player;
        private Holding _holding;
        private Offer _offer;
        private Prediction _prediction;
        private NewsItem _newsItem;

        #endregion
        #region Interface

        public void Buy(Player player, Prediction prediction, int numPredictions, int price, DateTime endDate)
        {
            if (player == null)
                throw new ArgumentNullException("player");
            if (prediction == null)
                throw new ArgumentNullException("prediction");

            if (!prediction.IsActive)
                throw new ArgumentException(String.Format("Prediction {0} is not active.", prediction.Ticker));
            if (prediction.IsClosed)
                throw new ArgumentException(String.Format("Prediction {0} is closed.", prediction.Ticker));
            if (numPredictions < 1)
                throw new ArgumentException(String.Format("Must buy at least 1 prediction of {0} a time.", prediction.Ticker));
            if (price < 1)
                throw new ArgumentException("Price must be a positive value. Tried to buy for " + price + ".");
            if (price > 99)
                throw new ArgumentException("Price must be lower than the maximum price of 100. Tried to buy for " + price + ".");
            if (endDate > prediction.EndDate)
                throw new ArgumentException("End date on offer must be before end date on prediction " + prediction.Ticker + ", which is " + prediction.EndDate + ".");
            if (player.Cash < (numPredictions * price))
                throw new ArgumentException(player.UserName + " does not have enough cash for this offer. It would cost " + (numPredictions * price) + " while " + player.UserName + " only has " + player.Cash + " to buy for.");

            _player = player;
            _prediction = prediction;
            PerformBuy(numPredictions, price, endDate);
            CreateNewsItem(true, numPredictions, price, endDate);
        }

        public void Sell(Player player, Holding holding, Prediction prediction, int numPredictions, int price, DateTime endDate)
        {
            if (player == null)
                throw new ArgumentNullException("player");
            if (holding == null)
                throw new ArgumentNullException("holding");
            if (prediction == null)
                throw new ArgumentNullException("prediction");

            if (! prediction.IsActive)
                throw new ArgumentException(String.Format("Prediction {0} is not active.", prediction.Ticker));
            if (prediction.IsClosed)
                throw new ArgumentException(String.Format("Prediction {0} is closed.", prediction.Ticker));
            if (numPredictions < 1)
                throw new ArgumentException(String.Format("Must sell at least 1 prediction of {0} a time.", prediction.Ticker));
            if (price < 1)
                throw new ArgumentException("Price must be a positive value. Tried to sell for " + price + ".");
            if (price > 99)
                throw new ArgumentException("Price must be lower than the maximum price of 100. Tried to sell for " + price + ".");
            if (endDate < DateTime.Today.AddDays(1))
                throw new ArgumentException("End date must be at least one day into the future. Tried to set end date as " + endDate + ".");
            if (endDate > prediction.EndDate)
                throw new ArgumentException("End date on offer must be before end date on prediction " + prediction.Ticker + ", which is " + prediction.EndDate + "."); 
            if (player.Id != holding.PlayerId)
                throw new ArgumentException(String.Format("{0} does not own this holding.", player.UserName));
            if (holding.PredictionId != prediction.Id)
                throw new ArgumentException(String.Format("Holding is not of prediction {0}.", prediction.Ticker));
            if (holding.NumPredictions < numPredictions)
                throw new ArgumentException(player.UserName + " only has " + holding.NumPredictions + " predictions to sell. Tried to sell " + numPredictions + " predictions."); 

            _player = player;
            _holding = holding;
            _prediction = prediction;
            PerformSell(numPredictions, price, endDate);
            CreateNewsItem(false, numPredictions, price, endDate);
        }

        #endregion
        #region Private methods

        private void CreateNewsItem(bool isBuy, int numPredictions, int price, DateTime endDate)
        {
            _newsItem = new NewsItem();
            _newsItem.PredictionId = _prediction.Id;
            _newsItem.NewsDate = DateTime.Now;
            _newsItem.ShouldBeShownInMainNews = false;
            _newsItem.Title = String.Format("New {0} offer.", _prediction.Ticker);
            _newsItem.Message = String.Format("New offer on {0}. You can now {1} {2} {0} {3} for {4}{5}. The offer is valid until {6}.", 
                _prediction.Ticker, 
                (isBuy ? "sell" : "buy"),
                numPredictions, 
                (numPredictions == 1) ? "stock" : "stocks",                
                price,
                (numPredictions == 1) ? "" : " each",
                endDate.ToShortDateString());
        }

        private void PerformSell(int numPredictions, int price, DateTime endDate)
        {
            _holding.NumPredictions -= numPredictions;

            _offer = new Offer();
            _offer.EndDate = endDate;
            _offer.IsAccepted = false;
            _offer.IsActive = true;
            _offer.IsBuy = false;
            _offer.NumPredictions = numPredictions;
            _offer.PlayerId = _player.Id;
            _offer.PredictionId = _holding.PredictionId;
            _offer.Price = price;          
        }

        private void PerformBuy(int numPredictions, int price, DateTime endDate)
        {
            _player.Cash -= (price * numPredictions);

            _offer = new Offer();
            _offer.EndDate = endDate;
            _offer.IsAccepted = false;
            _offer.IsActive = true;
            _offer.IsBuy = true;
            _offer.NumPredictions = numPredictions;
            _offer.PlayerId = _player.Id;
            _offer.PredictionId = _prediction.Id;
            _offer.Price = price;
        }

        #endregion
        #region Properties

        public Player NewOfferPlayer
        {
            get { return _player; }
        }

        public Holding NewOfferHolding
        {
            get { return _holding; }
        }

        public Offer NewOfferOffer
        {
            get { return _offer; }
        }

        public NewsItem NewOfferNewsItem
        {
            get { return _newsItem; }
        }

        #endregion
    }
}
